#pragma once

#include "viewer_dc.h"

namespace viewer
{
    struct ShortCut
    {
        virtual void drawHint(viewer_dc dc, cg::point_2i & pt) const = 0;
        virtual bool process (DWORD vkey) = 0;
        virtual     ~ShortCut() {}
    };

    namespace details
    {
        template <class Target>
            struct Trigger
        {
            Trigger (Target * tgt, Target const & defval) : target_ (tgt)
            {
                *tgt = defval;
            }

            void changeState()
            {
                *target_ = !*target_;
            }

            const char * state () const { return (*target_ ? "On" : "Off"); }

        private:
            Target  * target_;
        };

        template <class Derived>
            struct Hint
        {
            Hint (const char * hint, DWORD key) : hint_(hint)
            {
                char str[] = { ' ', '(', (char)key, ')', 0 };
                hint_ += str;
            }

            void drawHint(viewer_dc dc, cg::point_2i & pt) const
            {
                dc.putText(pt.x, pt.y) << hint_.c_str() << ": " << self().state();
                pt.y += 15;
            }

        private:
            std::string     hint_;

            Derived const & self() const { return static_cast<Derived const &>(*this); }
        };

        template <class Derived>
            struct KeyHandler
        {
            KeyHandler (DWORD key) : key_ (key) {}

            bool process (DWORD vkey)
            {
                if (vkey == key_)
                {
                    self().changeState();
                    return true;
                }
                return false;
            }

        private:
    
            DWORD   key_;

            Derived & self() { return static_cast<Derived &>(*this); }
        };
    }

    template <class Target>
        struct ShortCutImpl 
            :   ShortCut
            ,   details::Trigger    <Target>
            ,   details::Hint       <ShortCutImpl<Target> >
            ,   details::KeyHandler <ShortCutImpl<Target> >
    {
        ShortCutImpl (Target * target, const char * hint, DWORD key, Target defval)            
            :   details::Trigger    <Target>       (target, defval)
            ,   details::Hint       <ShortCutImpl> (hint, key)
            ,   details::KeyHandler <ShortCutImpl> (key)
        {}

        void drawHint(viewer_dc dc, cg::point_2i & pt) const 
        {   
            details::Hint<ShortCutImpl>::drawHint(dc, pt);
        }

        bool process (DWORD vkey)
        {
            return 
                details::KeyHandler<ShortCutImpl>::process(vkey);
        }
    };

    struct ShortCutManager
    {
        ShortCutManager & operator << (ShortCut * sh)
        {
            shortcuts_.push_back(sh);
            return *this;
        }

        void drawHint(viewer_dc dc, cg::point_2i & pt) const
        {
            dc->SetColor( cg::color_white(), .5f);

            if (show_)
            {
                dc.putText(pt.x, pt.y) << "Hide this hint (F1)" ; pt.y += 15;

                for (size_t i = 0; i != shortcuts_.size(); ++i)
                    shortcuts_[i]->drawHint(dc, pt);
            }
            else
            {
                dc.putText(pt.x, pt.y) << "Show hint (F1)" ; pt.y += 15;
            }
        }

        bool process (DWORD vkey)
        {
            if (vkey == VK_F1) {
                show_ = !show_;
                return true;
            }
            else
            {
                for (size_t i = 0; i != shortcuts_.size(); ++i)
                {
                    if (shortcuts_[i]->process(vkey))
                        return true;
                }
                return false;
            }
        }

        ShortCutManager () : show_ (true) {}

        ~ShortCutManager ()
        {
            for (size_t i = 0; i != shortcuts_.size(); ++i)
                delete shortcuts_[i];
        }

    private:
        std::vector<ShortCut*>  shortcuts_;
        bool                    show_;
    };
}
